package lelouet.sh3d.plugins.sweetDatacenter;

import gridlib.api.MachineModel;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import lelouet.sh3d.Utils;
import lelouet.sh3d.plugins.sweetDatacenter.gui.GeneralPropertiesPanel;
import lelouet.sh3d.plugins.sweetDatacenter.model.GElement;
import lelouet.sh3d.plugins.sweetDatacenter.model.GElementFactory;
import lelouet.sh3d.plugins.sweetDatacenter.model.GRack;
import lelouet.sh3d.plugins.sweetDatacenter.model.GServer;

import com.eteks.sweethome3d.model.CatalogPieceOfFurniture;
import com.eteks.sweethome3d.model.CollectionEvent;
import com.eteks.sweethome3d.model.CollectionListener;
import com.eteks.sweethome3d.model.Content;
import com.eteks.sweethome3d.model.FurnitureCatalog;
import com.eteks.sweethome3d.model.FurnitureCategory;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.HomePieceOfFurniture;
import com.eteks.sweethome3d.model.Selectable;
import com.eteks.sweethome3d.model.SelectionEvent;
import com.eteks.sweethome3d.model.SelectionListener;
import com.eteks.sweethome3d.plugin.Plugin;
import com.eteks.sweethome3d.plugin.PluginAction;
import com.eteks.sweethome3d.tools.ResourceURLContent;

public class SweetDatacenterPlugin extends Plugin {

	public final static int UTILS_FRAME_WIDTH = 400;
	public final static int UTILs_FRAME_HEIGHT = 800;
	public final static Dimension UTILS_FRAME_DIMENSION = new Dimension(
			UTILS_FRAME_WIDTH, UTILs_FRAME_HEIGHT);
	public final static String MODEL_CATEGORY = "models";

	public static final String SERVER_MODEL_3D_FILE = "/lelouet/sh3d/plugins/sweetDatacenter/ServersFiles/RackMountServer1U.obj";
	public static final String SERVER_MODEL_PLAN_FILE = "/lelouet/sh3d/plugins/sweetDatacenter/ServersFiles/basic_server.jpg";
	public static final String RACKS_DESCRIPTION_FILE = "lelouet.sh3d.plugins.sweetDatacenter.RacksCatalog";

	/** contains all the graphical elements to use as models */
	protected List<Content> models3D = new ArrayList<Content>();

	/** contains all the graphical elements to use as models */
	protected List<Content> modelsPlan = new ArrayList<Content>();

	/** The frame that shows informations about the selected element */
	protected Window modifFrame = null;
	protected GeneralPropertiesPanel modifPane = null;

	/** memory of all elements created and all mappings done */
	protected GElementFactory elementFactory = null;

	protected class UnselectContainedServers implements SelectionListener {

		@Override
		public void selectionChanged(SelectionEvent selectionEvent) {
			getHome().removeSelectionListener(this);
			try {
				boolean onlyServers = true;
				List<Selectable> selectedServers = new ArrayList<Selectable>();
				for (Object sel : selectionEvent.getSelectedItems()) {
					if (sel instanceof GRack) {
						onlyServers = false;
					} else if (sel instanceof GServer) {
						selectedServers.add((Selectable) sel);
					}
				}
				if (!onlyServers) {
					Home home = getHome();
					for (Selectable sel : selectedServers) {
						home.deselectItem(sel);
					}
				}
			} catch (RuntimeException e) {
				getHome().addSelectionListener(this);
				throw e;
			}
			getHome().addSelectionListener(this);
		}

	}

	/**
	 * a {@link CollectionListener} that replaces all new added
	 * {@link HomePieceOfFurniture} by {@link GElement} corresponding : any time
	 * a user adds a {@link CatalogPieceOfFurniture} from a
	 * {@link FurnitureCatalog}, that piece is set to be removed later and
	 * another piece, that replaces it, is created by the bridge and added.<br />
	 * Plus, it removes the deleted {@link GElement} from the model, too.
	 */
	protected class FurnitureToGElem implements
			CollectionListener<HomePieceOfFurniture> {

		public void collectionChanged(CollectionEvent<HomePieceOfFurniture> ev) {
			if (ev.getType() == CollectionEvent.Type.ADD) {
				final HomePieceOfFurniture item = ev.getItem();
				if (item instanceof GElement) {
					return;
				}
				final Home home = getHome();
				home.removeFurnitureListener(this);
				final HomePieceOfFurniture myPiece = elementFactory
						.convertElement(item);
				home.addPieceOfFurniture(myPiece, ev.getIndex());
				home.addFurnitureListener(this);
				// Invoked later because adding item to the home is being done.
				EventQueue.invokeLater(new Runnable() {
					public void run() {
						home.deletePieceOfFurniture(item);
						List<Selectable> selectedItems = new ArrayList<Selectable>();
						selectedItems.add(myPiece);
						home.setSelectedItems(selectedItems);
					}
				});
			} else if (ev.getType() == CollectionEvent.Type.DELETE) {
				final HomePieceOfFurniture item = ev.getItem();
				if (item instanceof GRack) {
					((GRack) item).graphicalDelete();
				} else if (item instanceof GServer) {
					((GServer) item).graphicalDelete();
				}
			}
		}
	}

	@Override
	public PluginAction[] getActions() {
		FurnitureCatalog catalog = getUserPreferences().getFurnitureCatalog();
		Utils.emptyCatalog(catalog);
		loadGraphicalModels();
		generateBaseModels();
		loadRacksModels(catalog);
		getHome().addFurnitureListener(new FurnitureToGElem());
		getHome().addSelectionListener(new UnselectContainedServers());
		elementFactory = new GElementFactory(catalog, getHome());

		modifPane = new GeneralPropertiesPanel(getGEFactory());
		for (SelectionListener sl : modifPane.getSelectionListener()) {
			getHome().addSelectionListener(sl);
		}
		modifFrame = new JFrame();
		modifFrame.add(modifPane);
		modifFrame.setSize(UTILS_FRAME_DIMENSION);
		modifFrame.setLocation((int) (Toolkit.getDefaultToolkit()
				.getScreenSize().getWidth()
				- modifFrame.getSize().getWidth() - 30), 20);
		modifFrame.setVisible(true);

		final LoadGridlibButton load_gridlib = new LoadGridlibButton(this);
		final SaveGridlibButton save_gridlib = new SaveGridlibButton(this);
		final SaveEntropyButton save_entropy = new SaveEntropyButton(this);
		final LoadEntropyButton load_entropy = new LoadEntropyButton(this);
		final CopyRackButton copy_rack = new CopyRackButton(this,
				getGEFactory());

		return new PluginAction[] {
				new PluginAction(
						"lelouet.sh3d.plugins.sweetDatacenter.ApplicationPlugin",
						"DESCRIBE_RACKS", getPluginClassLoader(), true) {
					@Override
					public void execute() {
						JOptionPane.showMessageDialog(null, elementFactory
								.describe());
					}
				},
				new PluginAction(
						"lelouet.sh3d.plugins.sweetDatacenter.ApplicationPlugin",
						"MODIF_FRAME_MANAGER", getPluginClassLoader(), true) {
					@Override
					public void execute() {
						modifFrame.setVisible(!modifFrame.isVisible());
					}
				},
				new PluginAction(
						"lelouet.sh3d.plugins.sweetDatacenter.ApplicationPlugin",
						"CLEAR_DATACENTER", getPluginClassLoader(), true) {
					@Override
					public void execute() {
						elementFactory.clear();
					}

				}, load_gridlib, save_gridlib, load_entropy, save_entropy,
				copy_rack };
	}

	protected GElementFactory getGEFactory() {
		return elementFactory;
	}

	/**
	 * load the graphical models that can be used for servers : icon model for
	 * the plan, and 3D model for the 3D view
	 */
	protected void loadGraphicalModels() {
		Content model3D = Utils.getContentInJar(getPluginClassLoader(),
				SERVER_MODEL_3D_FILE);
		models3D.add(model3D);
		Content modelPlan = Utils.getContentInJar(getPluginClassLoader(),
				SERVER_MODEL_PLAN_FILE);
		modelsPlan.add(modelPlan);
		for (Content c : models3D) {
			System.out.println(" 3D model : " + c.toString());
		}
		for (Content c : modelsPlan) {
			System.out.println(" plan model : " + c.toString());
		}
	}

	protected void generateBaseModels() {
		Content icon = (Content) modelsPlan.toArray()[0];
		Content model = (Content) models3D.toArray()[0];
		System.out.println(" base icon : "
				+ ((ResourceURLContent) icon).getURL() + " , model : "
				+ ((ResourceURLContent) model).getJAREntryName());
		CatalogPieceOfFurniture default1UServer = new CatalogPieceOfFurniture(
				Utils.getDefaultServerId(1), "basic server", "", icon, icon,
				model, 48.26f, 92.71f, 8.88f, 0, true, null, "", true,
				new BigDecimal(0.0), new BigDecimal(0));
		getUserPreferences().getFurnitureCatalog().add(
				new FurnitureCategory("servers"), default1UServer);
	}

	protected void loadRacksModels(FurnitureCatalog inCatalog) {
		final URL jarURL = ((URLClassLoader) getPluginClassLoader()).getURLs()[0];
		Utils.loadServerCatalog(RACKS_DESCRIPTION_FILE, jarURL, inCatalog);
	}

	/**
	 * add a model to generate servers from.
	 */
	public void addModel(MachineModel model) {
		CatalogPieceOfFurniture cpof = createModel(model);
		if (cpof != null) {
			getUserPreferences().getFurnitureCatalog().add(
					new FurnitureCategory(MODEL_CATEGORY), cpof);
		}
	}

	protected CatalogPieceOfFurniture createModel(MachineModel mmodel) {
		Content icon = (Content) modelsPlan.toArray()[0];
		Content model = (Content) models3D.toArray()[0];
		try {
			System.out.println("creating model " + mmodel.getName());
			return new CatalogPieceOfFurniture(mmodel.getName(), icon, model,
					48.26f, 92.71f, 8.88f, 0, true, null, null, true, 0, true);
		} catch (RemoteException e) {
			e.printStackTrace();
			return null;
		}
	}

}
